<?php

namespace yunj\app\admin\service\member;

use think\facade\Db;
use yunj\app\admin\enum\auth\AuthType;
use yunj\app\admin\enum\State;
use yunj\app\admin\enum\TableBuilderEvent;
use yunj\app\admin\service\auth\AuthService;
use yunj\app\admin\service\Service;
use yunj\app\admin\validate\member\MemberRoleValidate;
use yunj\core\builder\YunjForm;
use yunj\core\builder\YunjTable;
use yunj\core\enum\RedisKeyPrefixEnum;

class MemberRoleService extends Service {

    /**
     * 获取列表构建器
     * @return YunjTable
     */
    public function getListBuilder(): YunjTable {
        $args = [
            'state' => State::getTableBuilderState(),
            'page' => false,
            'filter' => function (YunjTable $builder, $state) {
                return [
                    'keywords' => [
                        'title' => '关键词',
                        'verify' => 'chsDash',
                        'placeholder' => '名称/别名'
                    ]
                ];
            },
            'toolbar' => function (YunjTable $builder, $state) {
                $toolbar = [
                    'add' => ['type' => 'openTab', 'title' => '添加', 'class' => 'layui-icon-add-circle', 'url' => build_url('yunjAdminMemberRoleAdd'), 'auth' => 'yunj_role_add']
                ];
                switch ($state) {
                    case State::NORMAL:
                        $toolbar += [
                            TableBuilderEvent::RECYLE_BIN => ['title' => '移入回收站', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_role_recyle_bin'],
                        ];
                        break;
                    case State::RECYLE_BIN:
                        $toolbar += [
                            TableBuilderEvent::NORMAL => ['title' => '恢复正常', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_role_normal'],
                            TableBuilderEvent::DELETED => ['title' => '永久删除', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_role_deleted'],
                        ];
                        break;
                }
                return $toolbar;
            },
            'defaultToolbar' => [
                'filter',
                'export' => [
                    'auth' => 'yunj_role_export'
                ]
            ],
            'cols' => function (YunjTable $builder, $state) {
                $cols = [
                    'id' => ['type' => 'checkbox'],
                    'name' => ['title' => '名称'],
                    'alias' => ['title' => '别名'],
                    'create_time' => ['title' => '添加时间', 'align' => 'center', 'templet' => 'datetime'],
                    'dragSort' => ['title' => '排序', 'align' => 'center', 'templet' => 'dragSort', 'auth' => 'yunj_role_sort'],
                    'action' => ['title' => '操作', 'templet' => 'action', 'options' => []]
                ];
                $actionOptions = [
                    'edit' => ['type' => 'openTab', 'title' => '详情', 'class' => 'layui-icon-survey', 'url' => build_url('yunjAdminMemberRoleEdit'), 'auth' => 'yunj_role_edit']
                ];
                switch ($state) {
                    case State::NORMAL:
                        $actionOptions += [
                            TableBuilderEvent::RECYLE_BIN => ['title' => '移入回收站', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_role_recyle_bin'],
                        ];
                        break;
                    case State::RECYLE_BIN:
                        $actionOptions += [
                            TableBuilderEvent::NORMAL => ['title' => '恢复正常', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_role_normal'],
                            TableBuilderEvent::DELETED => ['title' => '永久删除', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_role_deleted']
                        ];
                        break;
                }
                $cols['action']['options'] = $actionOptions;
                $this->setListBuilderColsAuth($cols);
                return $cols;
            },
            'validate' => MemberRoleValidate::class,
            'count' => function (YunjTable $builder, array $filter) {
                $where = self::getListBuilderFilterWhere($filter);
                return self::getAdminMemberRoleModel()->getOwnCount($where);
            },
            'items' => function (YunjTable $builder, int $page, int $pageSize, array $filter, array $sort) {
                $where = self::getListBuilderFilterWhere($filter);
                $order = $sort + ['sort' => 'aes'];
                $items = self::getAdminMemberRoleModel()->getOwnRowsToArray($where, ["*"], $order, $page, $pageSize);
                self::listBuilderItemsAppendAction($items, $filter['state']);
                return $items;
            },
            'event' => function (YunjTable $builder, $event, array $ids) {
                $res = TableBuilderEvent::handleEvent(self::getAdminMemberRoleModel(), $eventObj, $ids, function ($event, $ids, array $modelFunParams) {
                    return $this->listBuilderEventFilter($eventObj, $ids, $modelFunParams);
                });
                return $res ?: error_json("异常事件[{$event}]");
            },
        ];
        return YT('RoleList', $args);
    }

    /**
     * 设置列表构建器cols表头权限
     * @param array $cols
     */
    private function setListBuilderColsAuth(array &$cols): void {
        foreach ($cols as $k => $v) {
            if (in_array($k, ['id', 'action', 'dragSort'])) continue;
            $cols[$k]['auth'] = 'yunj_role_list_view_normal';
        }
    }

    /**
     * 列表构建器items追加action操作栏值
     * @param array $items
     */
    private static function listBuilderItemsAppendAction(array &$items, $state): void {
        foreach ($items as &$item) {
            // 系统定义拥有所有权限的角色，不允许执行其他操作
            if (in_array($item['alias'], AuthService::HAS_ALL_AUTH_ROLES, true)) {
                $item['action'] = ['edit'];
                continue;
            }
        }
    }

    /**
     * 获取列表构建器表单条件
     * @param array $filter
     * @return array
     */
    private static function getListBuilderFilterWhere(array $filter): array {
        $state = $filter['state'];
        $ids = $filter['ids'];
        $keywords = $filter['keywords'];
        $where = [];
        $where[] = $state ? ['state', '=', $state] : ['state', '<>', State::DELETED];
        if ($ids) $where[] = ['id', 'in', $ids];
        if ($keywords) $where[] = ['name|alias', 'like', '%' . $keywords . '%'];
        return $where;
    }

    /**
     * 列表构建器事件处理filter闭包
     * @param $eventObj
     * @param $ids
     * @param array $modelFunParams
     * @return array
     */
    private function listBuilderEventFilter($eventObj, $ids, array $modelFunParams) {
        $currTime = time();
        $eventVal = $eventObj->getValue();
        if ($eventVal == TableBuilderEvent::DELETED) {
            [$data, $where] = $modelFunParams;
            $data['alias'] = Db::raw("concat(`alias`,'_del_{$currTime}')");
            $where[] = ['alias', 'not in', AuthService::HAS_ALL_AUTH_ROLES];
            $modelFunParams = [$data, $where];
        } elseif ($eventVal == TableBuilderEvent::RECYLE_BIN) {
            [$data, $where] = $modelFunParams;
            $where[] = ['alias', 'not in', AuthService::HAS_ALL_AUTH_ROLES];
            $modelFunParams = [$data, $where];
        }
        return $modelFunParams;
    }

    /**
     * 获取表单构建器（系统定义的拥有所有权限的角色不能编辑提交）
     * @param bool $isEdit 是否编辑页
     * @return YunjForm
     */
    public function getFormBuilder(bool $isEdit = false): YunjForm {
        $args = [
            'field' => function (YunjForm $builder) use ($isEdit) {
                $field = [
                    'name' => ['title' => '名称', 'verify' => 'require|chsDash|max:128', 'desc' => '只能输入汉字、字母、数字和下划线_及破折号-'],
                    // 别名最多支持45字符串，后面的用于记录删除的标记
                    'alias' => ['title' => '别名', 'verify' => 'require|alphaDash|max:45', 'desc' => '只能输入字母、数字、下划线_及短横线-'],
                    'intro' => ['title' => '简介', 'type' => 'textarea', 'verify' => 'max:255'],
                ];
                if ($isEdit) {
                    // 编辑
                    $field['id'] = ['type' => 'hidden'];

                    // 判断当前角色是否系统定义拥有所有权限的角色
                    $roleData = $builder->getLoadValues();
                    if ($roleData && in_array($roleData['alias'], AuthService::HAS_ALL_AUTH_ROLES, true)) {
                        // 系统定义拥有所有权限的角色别名不允许编辑
                        $field['alias']['readonly'] = true;
                    } else {
                        // 设置权限选项字段
                        $this->setFormBuilderFieldAuthsFields($field);
                    }
                    // 判断是否有提交权限
                    if (!admin_member_auth_check('yunj_role_edit_submit')) {
                        // 无提交权限，全部只读
                        foreach ($field as &$v) $v['readonly'] = true;
                    }
                    // 设置字段权限
                    $this->setFormBuilderFieldAuth($field);
                } else {
                    $this->setFormBuilderFieldAuthsFields($field);
                }
                // 设置栅格布局
                foreach ($field as &$v) {
                    $v['grid'] = [12, "6 l3 r3", "4 l4 r4"];
                }
                return $field;
            },
            'validate' => MemberRoleValidate::class,
            'button' => function () use ($isEdit) {
                if ($isEdit) {
                    if (admin_member_auth_check('yunj_role_edit_submit')) $button = ['reset', 'submit'];
                } else {
                    $button = ['clear', 'submit'];
                }
                return $button ?? [];
            },
            'submit' => function (YunjForm $builder, array $data) {
                return $this->formBuilderSubmit($data);
            }
        ];
        if ($isEdit) {
            $args['load'] = function () {
                return $this->formBuilderLoad();
            };
        }
        return YF('RoleForm', $args);
    }

    // 设置表单构建器字段权限
    private function setFormBuilderFieldAuth(array &$field): void {
        // 权限后缀等于key的key集合
        $authSuffixKeyArr = ['alias'];
        // 权限后缀等于auths的key集合
        $authSuffixAuthsArr = ['top_auths', 'sidebar_auths', 'other_auths'];
        foreach ($field as $k => $v) {
            if (in_array($k, $authSuffixKeyArr)) {
                $authSuffix = $k;
            } elseif (in_array($k, $authSuffixAuthsArr)) {
                $authSuffix = 'auths';
            } else {
                $authSuffix = 'normal';
            }
            $field[$k]['auth'] = 'yunj_role_edit_detail_' . $authSuffix;
        }
    }

    // 设置表单权限选择字段
    private function setFormBuilderFieldAuthsFields(array &$field): void {
        // 权限选择
        [$topNodes, $sidebarNodes, $otherNodes] = self::getFormBuilderFieldTreeNodes();
        $treeArgs = ['type' => 'tree', 'allOptional' => true, 'retractLevel' => 1, 'maxHeight' => 350, 'nodeIdKey' => 'key', 'nodePidKey' => 'parent'];
        if ($topNodes) {
            $field['top_auths'] = ['title' => '顶部菜单权限', 'nodes' => $topNodes] + $treeArgs;
        }
        if ($sidebarNodes) {
            $field['sidebar_auths'] = ['title' => '侧边栏权限', 'nodes' => $sidebarNodes] + $treeArgs;
        }
        if ($otherNodes) {
            $field['other_auths'] = ['title' => '其他权限', 'nodes' => $otherNodes] + $treeArgs;
        }
    }

    /**
     * 获取表单权限树节点
     * @return array[]
     */
    private static function getFormBuilderFieldTreeNodes(): array {
        $auths = AuthService::getInstance()->getAllAuths();
        // 其他
        $otherNodes = [];
        foreach ($auths as $key => $auth) {
            if ($auth['parent'] || $auth['type'] != AuthType::NORMAL) continue;
            $otherNodes[] = $auth;
            self::_getFormBuilderFieldTreeNodes($key, $auths, $otherNodes);
        }
        // 菜单
        $topNodes = [];
        $sidebarNodes = [];
        $auths = array_sort($auths, 'menu_sort');   // 按菜单排序
        foreach ($auths as $key => $auth) {
            if ($auth['parent']) continue;
            switch ($auth['type']) {
                case AuthType::TOP_MENU:
                    $topNodes[] = $auth;
                    self::_getFormBuilderFieldTreeNodes($key, $auths, $topNodes);
                    break;
                case AuthType::SIDEBAR_MENU:
                    $sidebarNodes[] = $auth;
                    self::_getFormBuilderFieldTreeNodes($key, $auths, $sidebarNodes);
                    break;
            }
        }
        return [$topNodes, $sidebarNodes, $otherNodes];
    }

    private static function _getFormBuilderFieldTreeNodes(string $parent, array $auths, array &$nodes): void {
        // 判断是否有子权限，防止重新去循环查询
        $parentHasSub = AuthService::getInstance()->isHasSub($parent);
        if (!$parentHasSub) return;

        foreach ($auths as $key => $auth) {
            if ($auth['parent'] != $parent) continue;
            $nodes[] = $auth;
            self::_getFormBuilderFieldTreeNodes($key, $auths, $nodes);
            $parentHasSub = true;
        }

        AuthService::getInstance()->setHasSub($parent, $parentHasSub);
    }

    /**
     * 表单构建器load
     * @return array|string
     */
    private function formBuilderLoad() {
        $id = request()->get('id');
        if (!$id) return '数据异常';
        $data = self::getAdminMemberRoleModel()->getOwnRowToArray([
            ['id', '=', $id],
            ['state', '<>', State::DELETED],
        ], ['id', 'name', 'alias', 'intro', 'auths']);
        if (!$data) return '数据异常';
        $data['top_auths'] = $data['sidebar_auths'] = $data['other_auths'] = $data['auths'];
        return $data;
    }

    /**
     * 表单构建器submit
     * @param array $data
     * @return \yunj\core\response\Json
     */
    private function formBuilderSubmit(array $data) {
        try {
            $isEdit = isset($data['dbRoleData']['id']);
            $roleModel = self::getAdminMemberRoleModel();
            if ($data['dbRoleData']) $isEdit ? $roleModel->change($data['dbRoleData']) : $roleModel->addRow($data['dbRoleData']);
            return success_json(["reload" => true], '角色' . ($isEdit ? '修改' : '新增') . '成功');
        } catch (\Exception $e) {
            log_exception($e);
            return error_json('角色' . ($isEdit ? '修改' : '新增') . '失败');
        }
    }

}